package kmi.droid.livewallpaper.gl;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;

import kmi.droid.livewallpaper.gl.EGLFactory.EGLContextFactory;
import kmi.droid.livewallpaper.gl.EGLFactory.EGLWindowSurfaceFactory;

import android.util.Log;
import android.view.SurfaceHolder;

public class EGLHelper
{
	private EGL10 _egl;
	private EGLDisplay _eglDisplay;
	private EGLSurface _eglSurface;
	private EGLContext _eglContext;
	private EGLConfig _eglConfig;

	private EGLConfigChooserBase _eglConfigChooser;
	private EGLContextFactory _eglContextFactory;
	private EGLWindowSurfaceFactory _eglWindowSurfaceFactory;
	private GLWrapper _glWrapper;

	public EGLHelper(EGLConfigChooserBase chooser,
			EGLContextFactory contextFactory,
			EGLWindowSurfaceFactory surfaceFactory, GLWrapper wrapper)
	{
		_eglConfigChooser = chooser;
		_eglContextFactory = contextFactory;
		_eglWindowSurfaceFactory = surfaceFactory;
		_glWrapper = wrapper;
	}

	public void start()
	{
		int instanceId = 0;
		Log.d("EglHelper" + instanceId, "start()");

		if (_egl == null)
		{
			Log.d("EglHelper" + instanceId, "getting new EGL");
			_egl = (EGL10) EGLContext.getEGL();
		} else
		{
			Log.d("EglHelper" + instanceId, "reusing EGL");
		}

		if (_eglDisplay == null)
		{
			Log.d("EglHelper" + instanceId, "getting new display");
			_eglDisplay = _egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
		} else
		{
			Log.d("EglHelper" + instanceId, "reusing display");
		}

		if (getEglConfig() == null)
		{
			Log.d("EglHelper" + instanceId, "getting new config");

			int[] version = new int[2];

			_egl.eglInitialize(_eglDisplay, version);
			setEglConfig(_eglConfigChooser.chooseConfig(_egl, _eglDisplay));
		} else
		{
			Log.d("EglHelper" + instanceId, "reusing config");
		}

		if (_eglContext == null)
		{
			Log.d("EglHelper" + instanceId, "creating new context");

			_eglContext = _eglContextFactory.createContext(_egl, _eglDisplay,
					getEglConfig());

			if (_eglContext == null || _eglContext == EGL10.EGL_NO_CONTEXT)
				throw new RuntimeException("createContext failed");

		} else
			Log.d("EglHelper" + instanceId, "reusing context");

		_eglSurface = null;
	}

	/*
	 * React to the creation of a new surface by creating and returning an
	 * OpenGL interface that renders to that surface.
	 */
	public GL createSurface(SurfaceHolder holder)
	{
		if (_eglSurface != null && _eglSurface != EGL10.EGL_NO_SURFACE)
		{
			_egl.eglMakeCurrent(_eglDisplay, EGL10.EGL_NO_SURFACE,
					EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
			_eglWindowSurfaceFactory.destroySurface(_egl, _eglDisplay,
					_eglSurface);
		}

		_eglSurface = _eglWindowSurfaceFactory.createWindowSurface(_egl,
				_eglDisplay, getEglConfig(), holder);

		if (_eglSurface == null || _eglSurface == EGL10.EGL_NO_SURFACE)
			throw new RuntimeException("createWindowSurface failed");

		if (!_egl.eglMakeCurrent(_eglDisplay, _eglSurface, _eglSurface,
				_eglContext))
			throw new RuntimeException("eglMakeCurrent failed.");

		GL gl = _eglContext.getGL();

		if (_glWrapper != null)
			gl = _glWrapper.wrap(gl);

		return gl;
	}

	/**
	 * Display the current render surface.
	 * 
	 * @return false if the context has been lost.
	 */
	public boolean swap()
	{
		_egl.eglSwapBuffers(_eglDisplay, _eglSurface);

		/*
		 * Always check for EGL_CONTEXT_LOST, which means the context and all
		 * associated data were lost (For instance because the device went to
		 * sleep). We need to sleep until we get a new surface.
		 */
		return _egl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
	}

	public void destroySurface()
	{
		if (_eglSurface != null && _eglSurface != EGL10.EGL_NO_SURFACE)
		{
			_egl.eglMakeCurrent(_eglDisplay, EGL10.EGL_NO_SURFACE,
					EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
			_eglWindowSurfaceFactory.destroySurface(_egl, _eglDisplay,
					_eglSurface);
			_eglSurface = null;
		}
	}

	public void finish()
	{
		if (_eglContext != null)
		{
			_eglContextFactory.destroyContext(_egl, _eglDisplay, _eglContext);
			_eglContext = null;
		}
		if (_eglDisplay != null)
		{
			_egl.eglTerminate(_eglDisplay);
			_eglDisplay = null;
		}
	}

	public EGLConfig getEglConfig()
	{
		return _eglConfig;
	}

	public void setEglConfig(EGLConfig _eglConfig)
	{
		this._eglConfig = _eglConfig;
	}
}
